home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Sounds Terrific 2
/
Sounds Terrific II (1996)(Weird Science)(Disc 1 of 2)[Amiga-PC].iso
/
archives
/
amiga
/
tracker_4_31.lzh
/
tracker
/
main.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-05-15
|
17KB
|
648 lines
/* main.c
vi:ts=3 sw=3:
*/
/* plays sound/noisetracker files on Sparc, silicon graphics.
* Authors : Liam Corner - zenith@dcs.warwick.ac.uk
* Marc Espie - espie@ens.fr
* Steve Haehnichen - shaehnic@ucsd.edu
* Andrew Leahy - alf@st.nepean.uws.edu.au
*
* Usage : tracker <filename>
* this version plays compressed files as well.
*/
/* $Id: main.c,v 4.25 1995/05/15 12:20:00 espie Exp espie $
* $Log: main.c,v $
* Revision 4.25 1995/05/15 12:20:00 espie
* *** empty log message ***
*
* Revision 4.24 1995/04/22 17:50:20 espie
* *** empty log message ***
*
* Revision 4.23 1995/03/17 00:32:22 espie
* TERM from #ifdef to pref option.
*
* Revision 4.22 1995/03/11 23:06:33 espie
* version changed.
*
* Revision 4.21 1995/03/06 22:35:38 espie
* Color can be default.
*
* Revision 4.20 1995/03/01 15:24:51 espie
* options -half/-double.
*
* Revision 4.19 1995/02/24 15:36:39 espie
* Case forgotten: if file does not open, song is not set.
*
* Revision 4.18 1995/02/21 21:13:16 espie
* Cleaned up source. Moved minor pieces of code around.
*
* Revision 4.17 1995/02/21 17:54:32 espie
* Internal problem: buggy RCS. Fixed logs.
*
* Revision 4.13 1995/02/15 14:30:09 espie
* optarg being NULL not checked -> segmentation.
*
* Revision 4.11 1995/02/14 04:02:28 espie
* Added version number.
*
* Revision 4.9 1995/02/01 20:41:45 espie
* Added color.
*
* Revision 4.8 1995/02/01 16:39:04 espie
* Loading formats thru rewind.
*
* Revision 4.5 1994/08/23 18:19:46 espie
* Added speed mode option.
* Added looping option.
* Nice OPT_CUT/OPT_ADD.
* Slight input/output changes.
* No more call to create_notes_table().
* Some notice to status.
* Use new pref scheme.
* Use info facility instead of printf for usage message.
* Options changes.
* Changed extended file semantics.
* Amiga support.
* Fixed upo previous song bug.
* Added bg/fg test.
* Added loads of new options.
* Added finetune.
*
* Revision 2.20 1992/11/17 17:06:25 espie
* Added PREVIOUS_SONG handling ???
* Use streamio for new interface (obsolescent signal handlers), and
* related changes.
* Cleaned up path reader, and better signal handling.
* Support for open_file.
* Added imask.
* Use transparent decompression/path lookup through open_file/close_file.
* Added setup_audio().
* Added some frequency/oversample/stereo change on the fly.
* Necessitates rightful closing/reopening of audio.
* Added compression methods. Changed getopt.
* Separated mix/stereo stuff.
* Added transpose feature.
* Added possibility to get back to MONO for the sgi.
* Added stereo capabilities to the indigo version.
* Added recovery and reread for automatic recognition
* of old/new tracker files.
* Added two level of fault tolerancy.
* Added more rational options.
* Moved almost everything to audio and automaton.
* Structured part of the code, especially replay ``automaton''
* and setting up of effects.
*
* Revision 1.26 1991/11/17 17:09:53 espie
* Added missing prototypes.
* Some more info while loading files.
* Added FAULT env variable, FAULT resistant playing,
* for playing modules which are not quite correct.
* Serious bug: dochangespeed was not reset all the time.
* Check all these parameters, they MUST be reset for
* each new song.
* Fixed a stupid bug: when env variable LOOPING was
* undefined, we got a segv on strcmp.
* Now we just test for its existence, since this is
* about all we want...
* Bug correction: when doing arpeggio, there might not
* be a new note, so we have to save the old note value
* and do the arppeggio on that note.
* Completely added control with OVERSAMPLE and FREQUENCY.
* Added control flow.
* Added pipe decompression, so that now you can do
* str32 file.Z directly.
* stdin may go away.
* Added arpeggio.
* Added vibslide and portaslide.
* Added speed command.
* Added signal control.
* Error checking: there shouldn't be that many
* segv signals any more.
* Moved every command to commands.c.
* Added some debug code for showing the full
* sequence for a file.
* Corrected the bug in volume slide: there is
* no default value, i.e., if it is 0, it is 0,
* as stupid as it may seem.
* Added vibrato.
* Added fastskip/corrected skip.
* Modified control flow of the player till
* it looks like something reasonable (i.e.,
* the structure is more natural and reflects
* the way stuff is played actually...)
* Do not restart the sound when we change instruments
* on the fly. A bit strange, but it works that way.
* Modified main to use new data structures.
* The sound player is MUCH cleaner, it uses now
* a 3-state automaton for each voice.
* Corrected ruckus with data type of sample.
*/
#include "defs.h"
extern char *VERSION;
#include <signal.h>
#include <ctype.h>
#ifdef VOLUME_CONTROL
#ifdef __hpux
#define true /* kludge to avoid typedef of boolean (name clash with macro) */
#include <audio/Alib.h>
#undef true
AGainDB volume = -20;
char use_speaker = 0;
#endif
#endif
#include "song.h"
#include "extern.h"
#include "options.h"
#include "getoption.h"
#include "tags.h"
#include "prefs.h"
ID("$Id: main.c,v 4.25 1995/05/15 12:20:00 espie Exp espie $")
LOCAL void print_usage()
{
GENERIC handle;
handle = begin_info("Usage");
infos(handle, "This is tracker ");
info(handle, VERSION);
info(handle, "This program is NOT to be redistributed");
info(handle, " without the full documentation");
info(handle, "");
info(handle, "Usage: tracker [options] filename [...]");
info(handle, "-help Display usage information");
info(handle, "-picky Do not tolerate any faults (default is to ignore most)");
info(handle, "-tolerant Ignore all faults");
info(handle, "-mono Select single audio channel output");
info(handle, "-stereo Select dual audio channel output");
info(handle, "-verbose Show text representation of song");
info(handle, "-repeats <count> Number of repeats (0 is forever) (default 1)");
info(handle, "-loop Loops the song list (plays again and again)");
info(handle, "-speed <speed> Song speed. Some songs want 60 (default 50)");
info(handle, "-mix <percent> Percent of channel mixing. (0 = spatial, 100 = mono)");
info(handle, "-new -old -both Select default reading type (default is -both)");
info(handle, "-frequency <freq> Set playback frequency in KHz");
info(handle, "-oversample <times> Set oversampling factor");
info(handle, "-transpose <n> Transpose all notes up");
info(handle, "-scroll Show what's going on");
info(handle, "-color Ansi color scrolling");
info(handle, "-sync Try to synch audio output with display");
#ifdef VOLUME_CONTROL
info(handle, "-speaker Output audio to internal speaker");
info(handle, "-volume <n> Set volume in dB");
#endif
info(handle, "");
info(handle, "RunTime:");
info(handle, "e,x exit program");
info(handle, "n next song");
info(handle, "p restart/previous song");
info(handle, "> fast forward");
info(handle, "< rewind");
info(handle, "S NTSC tempo\t s\tPAL tempo");
end_info(handle);
}
/* Command-line options. */
LOCAL struct long_option long_options[] =
{
{"help", 0, 'H', OPT_HELP},
{"picky", 0, 'P', OPT_PICKY},
{"tolerant", 0, 'L', OPT_TOLERANT},
{"new", 0, 'N', OPT_NEWONLY},
{"old", 0, 'O', OPT_OLDONLY},
{"both", 0, 'B', OPT_BOTH},
{"mono", 0, 'M', OPT_MONO},
{"stereo", 0, 'S', OPT_STEREO},
{"verbose", 0, 'V', OPT_VERBOSE},
{"frequency", 1, 'f', OPT_FREQUENCY},
{"oversample", 1, 'o', OPT_OVERSAMPLE},
{"transpose", 1, 't', OPT_TRANSPOSE},
{"repeats", 1, 'r', OPT_REPEATS},
{"speed", 1, 's', OPT_SPEED},
{"mix", 1, 'm', OPT_MIX},
{"start", 1, 'X', OPT_START},
{"cut", 1, '-', OPT_CUT},
{"add", 1, '+', OPT_ADD},
{"half", 1, '/', OPT_HALF},
{"double", 1, '2', OPT_DOUBLE},
{"scroll", 0, 'v', OPT_SHOW},
{"color", 0, 0, OPT_COLOR},
{"bw", 0, 0, OPT_BW},
{"xterm", 0, 0, OPT_XTERM},
{"noxterm", 0, 0, OPT_NOXTERM},
{"sync", 0, '=', OPT_SYNC},
{"loop", 0, 'l', OPT_LOOP},
{"speedmode", 1, '?', OPT_SPEEDMODE},
#ifdef VOLUME_CONTROL
{"speaker", 0, '#', OPT_SPEAKER},
{"volume", 1, 'u', OPT_VOLUME},
#endif
{0, 0, 0 , 0}
};
/* global variable to catch various types of errors and achieve the
* desired flow of control
*/
int error;
LOCAL unsigned long half_mask = 0;
/* v = optvalue(def):
* obtain a numeric value for an option, with default being def
*/
LOCAL int optvalue(def)
int def;
{
int d;
if (optarg && sscanf(optarg, "%d", &d) == 1)
return d;
else
{
optind--;
return def;
}
}
/* some preferences that don't need to be global */
LOCAL int ask_freq; /* parameters for setup audio */
LOCAL int oversample;
LOCAL int stereo;
LOCAL int start; /* parameters for st_play */
LOCAL int transpose;
LOCAL int loop = FALSE; /* main automaton looping at end of argv ? */
LOCAL void parse_options(argc, argv)
int argc;
char *argv[];
{
int c;
while ((c = getlongopt(argc, argv, long_options))
!= BAD_OPTION)
switch(c)
{
case OPT_LOOP:
loop = TRUE;
break;
case OPT_CUT:
case OPT_ADD:
case OPT_HALF:
{
int i;
unsigned long imask = 0;
for (i = 0; optarg && optarg[i]; i++)
{
char c = tolower(optarg[i]);
if (c >= '1' && c <= '9')
imask |= 1<< (c-'0');
else if (c >= 'a' && c <= 'z')
imask |= 1 << (c-'a'+10);
}
switch(c)
{
case OPT_ADD:
set_pref_scalar(PREF_IMASK, ~imask);
break;
case OPT_CUT:
set_pref_scalar(PREF_IMASK, imask);
break;
case OPT_HALF:
half_mask = imask;
break;
case OPT_DOUBLE:
half_mask = ~imask;
}
}
break;
case OPT_SPEEDMODE:
if (optarg)
{
if (stricmp(optarg, "old") == 0)
set_pref_scalar(PREF_SPEEDMODE, OLD_SPEEDMODE);
else if (stricmp(optarg, "normal") == 0)
set_pref_scalar(PREF_SPEEDMODE, NORMAL_SPEEDMODE);
else if (stricmp(optarg, "finefirst") == 0)
set_pref_scalar(PREF_SPEEDMODE, FINESPEED_ONLY);
else if (stricmp(optarg, "normalfirst") == 0)
set_pref_scalar(PREF_SPEEDMODE, SPEED_ONLY);
}
break;
case OPT_OLDONLY: /* old tracker type */
set_pref_scalar(PREF_TYPE, OLD);
break;
case OPT_NEWONLY: /* new tracker type */
set_pref_scalar(PREF_TYPE, NEW);
break;
case OPT_SHOW:
set_pref_scalar(PREF_SHOW, TRUE);
break;
case OPT_XTERM:
set_pref_scalar(PREF_XTERM, TRUE);
break;
case OPT_NOXTERM:
set_pref_scalar(PREF_XTERM, FALSE);
break;
case OPT_COLOR:
set_pref_scalar(PREF_COLOR, TRUE);
break;
case OPT_BW:
set_pref_scalar(PREF_COLOR, FALSE);
break;
case OPT_SYNC:
set_pref_scalar(PREF_SYNC, TRUE);
break;
case OPT_BOTH: /* both tracker types */
set_pref_scalar(PREF_TYPE, BOTH);
break;
case OPT_REPEATS: /* number of repeats */
set_pref_scalar(PREF_REPEATS, optvalue(0));
break;
case OPT_SPEED:
set_pref_scalar(PREF_SPEED, optvalue(50));
break;
case OPT_MONO:
stereo = FALSE;
break;
case OPT_STEREO:
stereo = TRUE;
break;
case OPT_OVERSAMPLE:
oversample = optvalue(1);
break;
case OPT_FREQUENCY:
ask_freq = optvalue(0) * 1000;
break;
case OPT_TRANSPOSE:
transpose = optvalue(0);
break;
case OPT_PICKY:
set_pref_scalar(PREF_TOLERATE, 0);
break;
case OPT_TOLERANT:
set_pref_scalar(PREF_TOLERATE, 2);
break;
case OPT_MIX: /* % of channel mix.
* 0->full stereo, 100->mono */
set_mix(optvalue(30));
break;
case OPT_START:
start = optvalue(0);
break;
case OPT_HELP:
print_usage();
end_all(0);
/* NOTREACHED */
case OPT_VERBOSE:
set_pref_scalar(PREF_DUMP, TRUE);
break;
#ifdef VOLUME_CONTROL
case OPT_VOLUME:
volume = optvalue(-20);
break;
case OPT_SPEAKER:
use_speaker = 1;
break;
#endif
}
}
/* song = load_song(filename):
* syntactic sugar around read_song
* - display the file name after stripping the path
* - find the actual file
* - read the song trying several formats
* - handle errors gracefully
*/
LOCAL struct song *load_song(name)
char *name;
{
struct song *song;
char *buffer;
struct exfile *file;
int i, j;
/* display the file name */
i = strlen(name);
for (j = i; j > 0; j--)
if (name[j] == '/' || name[j] == '\\')
{
j++;
break;
}
buffer = malloc( i - j + 5);
if (buffer)
{
sprintf(buffer, "%s...", name + j);
status(buffer);
}
/* read the song */
file = open_file(name, "r", getenv("MODPATH"));
if (file)
{
switch(get_pref_scalar(PREF_TYPE))
{
case BOTH:
song = read_song(file, NEW);
if (song)
break;
else
rewind_file(file);
/* FALLTHRU */
case OLD:
song = read_song(file, OLD);
break;
/* this is explicitly flagged as a new module,
* so we don't need to look for a signature.
*/
case NEW:
song = read_song(file, NEW_NO_CHECK);
break;
default:
song = NULL;
}
close_file(file);
}
else
song = NULL;
/* remove the displayed file name */
if (buffer)
{
status(0);
free(buffer);
}
return song;
}
/* initialize all options to default values */
LOCAL void set_default_prefs()
{
char *s;
start = 0;
set_pref_scalar(PREF_IMASK, 0);
set_pref_scalar(PREF_BCDVOL, 0);
set_pref_scalar(PREF_DUMP, FALSE);
set_pref_scalar(PREF_SHOW, FALSE);
#ifdef COLOR_IS_DEFAULT
set_pref_scalar(PREF_COLOR, TRUE);
#else
set_pref_scalar(PREF_COLOR, FALSE);
#endif
set_pref_scalar(PREF_SYNC, FALSE);
set_pref_scalar(PREF_TYPE, BOTH);
set_pref_scalar(PREF_REPEATS, 1);
set_pref_scalar(PREF_SPEED, 50);
set_pref_scalar(PREF_TOLERATE, 1);
set_pref_scalar(PREF_SPEEDMODE, NORMAL_SPEEDMODE);
s = getenv("TERM");
if (strncmp(s, "xterm", 5) == 0 || strncmp(s, "kterm", 5) == 0
|| strncmp(s, "cxterm", 6) == 0)
set_pref_scalar(PREF_XTERM, TRUE);
else
set_pref_scalar(PREF_XTERM, FALSE);
ask_freq = read_env("FREQUENCY", 0);
oversample = read_env("OVERSAMPLE", 1);
transpose = read_env("TRANSPOSE", 0);
stereo = !getenv("MONO");
set_mix(30);
}
LOCAL void adjust_song(s, m)
struct song *s;
unsigned long m;
{
int i, j ;
for (i = 1; i <= s->ninstr; i++)
if ( (1 << i) & ~m)
{
for (j = 0; j <= MAX_VOLUME; j++)
s->samples[i]->volume_lookup[j] *= 2;
}
s->side_width++;
}
int main(argc, argv)
int argc;
char **argv;
{
struct song *song;
int *is_song;
int i;
struct tag *result;
EXPAND_WILDCARDS(argc,argv);
is_song = (int *)malloc(sizeof(int) * argc);
if (!is_song)
end_all("No memory left");
for (i = 0; i < argc; i++)
is_song[i] = FALSE; /* For termination */
set_default_prefs();
if (argc == 1)
{
print_usage();
end_all(0);
}
looping:
for (optind = 1; optind < argc; optind++)
{
parse_options(argc, argv);
if (optind >= argc)
end_all(0);
song = load_song(argv[optind]);
if (song)
is_song[optind] = TRUE;
else
{
puts("not a song");
is_song[optind] = FALSE;
continue;
}
play_on:
if (get_pref_scalar(PREF_DUMP))
dump_song(song);
if (half_mask)
adjust_song(song, half_mask);
transpose_song(song, transpose);
setup_audio(ask_freq, stereo, oversample);
result = play_song(song, start);
release_song(song);
status(0);
while (result = get_tag(result))
{
switch (result->type)
{
case PLAY_PREVIOUS_SONG:
optind--;
while ((optind > 0) && !is_song[optind])
optind--;
if (optind == 0)
{
if (loop)
optind = argc - 1;
else
end_all(0);
}
song = load_song(argv[optind]);
goto play_on;
/* NOTREACHED */
case PLAY_LOAD_SONG:
song = load_song(result->data.pointer);
free(result->data.pointer);
if (song)
goto play_on;
default:
break;
}
result++;
}
}
if (loop)
goto looping;
end_all(0);
/* NOTREACHED */
}